/*=============================================================================*
 *  Copyright 2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *=============================================================================*/
package topics.expression.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import topics.Topic;
import topics.TopicSpaceSet;
import topics.expression.InvalidTopicExpressionException;
import topics.expression.TopicExpression;
import topics.expression.TopicExpressionEngine;
import topics.expression.TopicExpressionEvaluator;
import topics.expression.TopicExpressionException;
import topics.expression.TopicExpressionResolutionException;
import topics.expression.TopicPathDialectUnknownException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Resolves topic expressions over topic lists. The engine looks for topic
 * expression evaluators under "wsrf/topic/eval" context.
 * 
 * @see topics.TopicSpaceSet
 */
public class TopicExpressionEngineImpl implements TopicExpressionEngine {
	private static TopicExpressionEngine m_engine = null;
	private static final Log LOG = LogFactory
			.getLog(TopicExpressionEngineImpl.class.getName());
	private Map m_evaluators = new HashMap();

	/**
	 * Creates a new {@link TopicExpressionEngineImpl} object.
	 */
	public TopicExpressionEngineImpl() {
		this.registerEvaluator(new SimpleTopicExpressionEvaluator());
		this.registerEvaluator(new ConcreteTopicExpressionEvaluator());
		this.registerEvaluator(new FullTopicExpressionEvaluator());
	}

	/**
	 * Get the default topic expression engine instance (currently used to hide
	 * JNDI lookup details, may change in the future)
	 * 
	 * @return The default topic expression engine instance
	 */
	public static TopicExpressionEngine getInstance(  )
   {
	   if(m_engine == null) {
		   m_engine = new TopicExpressionEngineImpl();
		   return m_engine;
	   }
	   else {
		   return m_engine;
	   }
      /*try
      {
         Context context = new InitialContext(  );
         return (TopicExpressionEngine) context.lookup( JndiConstants.KEY_NAME_DEFAULT_TOPIC_EXPRESSION_ENGINE );
      }
      catch ( NamingException ne )
      {
         LOG.warn( "topicEngineConfigError: " + ne );
         return new TopicExpressionEngineImpl(  );
      }*/
   }

	/**
	 * DOCUMENT_ME
	 * 
	 * @param dialect
	 *            DOCUMENT_ME
	 * 
	 * @return DOCUMENT_ME
	 */
	public synchronized TopicExpressionEvaluator getEvaluator(String dialect) {
		TopicExpressionEvaluator evaluator = (TopicExpressionEvaluator) this.m_evaluators
				.get(dialect);
		return evaluator;
	}

	/**
	 * DOCUMENT_ME
	 * 
	 * @return DOCUMENT_ME
	 */
	public synchronized String[] getSupportedDialects() {
		return (String[]) this.m_evaluators.keySet().toArray(
				new String[this.m_evaluators.size()]);
	}

	/**
	 * DOCUMENT_ME
	 * 
	 * @param topicSpaceSet
	 *            DOCUMENT_ME
	 * @param topicExpression
	 *            DOCUMENT_ME
	 * 
	 * @return DOCUMENT_ME
	 * 
	 * @throws topics.expression.TopicPathDialectUnknownException
	 *             DOCUMENT_ME
	 * @throws TopicExpressionResolutionException
	 *             DOCUMENT_ME
	 * @throws InvalidTopicExpressionException
	 *             DOCUMENT_ME
	 * @throws TopicExpressionException
	 *             DOCUMENT_ME
	 * @throws TopicPathDialectUnknownException
	 *             DOCUMENT_ME
	 */
	public synchronized Topic[] evaluateTopicExpression(
			TopicSpaceSet topicSpaceSet, TopicExpression topicExpression)
			throws topics.expression.TopicPathDialectUnknownException,
			TopicExpressionResolutionException,
			InvalidTopicExpressionException, TopicExpressionException {
		if (topicExpression == null) {
			throw new InvalidTopicExpressionException("nullArgument"
					+ "topicExpression");
		}

		if (topicExpression.getDialect() == null) {
			throw new topics.expression.TopicPathDialectUnknownException(
					"nullArgument" + "topicExpression.dialect");
		}

		String dialect = topicExpression.getDialect().toString();
		TopicExpressionEvaluator evaluator = getEvaluator(dialect);

		if (evaluator == null) {
			if (LOG.isDebugEnabled()) {
				LOG.debug("Dialect not supported:" + dialect);
				LOG.debug("Registered dialects are:");
				Iterator keyIterator = m_evaluators.keySet().iterator();
				Object key = null;
				while (keyIterator.hasNext()) {
					key = keyIterator.next();
					LOG.debug(key);
				}

				LOG.debug("key.equals(dialect): "
						+ (key.equals(dialect.toString())));
				LOG.debug("this.evaluators.containsKey(dialect): "
						+ this.m_evaluators.containsKey(dialect.toString()));
				LOG.debug("HashCode of key: " + String.valueOf(key.hashCode()));
				LOG.debug("HashCode of dialect: "
						+ String.valueOf(dialect.toString().hashCode()));
				LOG.debug("Object stored for key: "
						+ this.m_evaluators.get(key));
			}

			throw new TopicPathDialectUnknownException("The dialect " + dialect
					+ " was not recognized.");
		}

		return evaluator.evaluate(topicSpaceSet, topicExpression);
	}

	/**
	 * Refresh the set of registered topic expression evaluators using
	 * information discovered from the JNDI registry. This method removes any
	 * previously registered topic expression evaluators, so handle with care.
	 */
	/*public synchronized void refresh() {
		m_evaluators.clear();
		NamingEnumeration list = null;
		try {
			Context context = new InitialContext();
			list = context
					.list(JndiConstants.TOPIC_EXPRESSION_EVALUATOR_CONTEXT);
			while (list.hasMore()) {
				NameClassPair pair = (NameClassPair) list.next();
				TopicExpressionEvaluator evaluator = (TopicExpressionEvaluator) JNDIUtils
						.lookup(context,
								JndiConstants.TOPIC_EXPRESSION_EVALUATOR_CONTEXT
										+ "/" + pair.getName(),
								TopicExpressionEvaluator.class);
				registerEvaluator(evaluator);
			}
		} catch (NamingException ne) {
			LOG.warn("topicEngineInitError: " + ne);
			registerEvaluator(new SimpleTopicExpressionEvaluator());
			registerEvaluator(new ConcreteTopicExpressionEvaluator());
			registerEvaluator(new FullTopicExpressionEvaluator());
		} finally {
			if (list != null) {
				try {
					list.close();
				} catch (NamingException ne) {
				}
			}
		}
	}*/

	/**
	 * DOCUMENT_ME
	 * 
	 * @param evaluator
	 *            DOCUMENT_ME
	 */
	public synchronized void registerEvaluator(
			TopicExpressionEvaluator evaluator) {
		LOG.debug("Adding dialects for " + evaluator.getClass().getName());
		String[] dialects = evaluator.getDialects();
		for (int i = 0; i < dialects.length; i++) {
			LOG.debug("Adding dialect: " + dialects[i]);
			this.m_evaluators.put(dialects[i], evaluator);
		}
	}
}